/*
 * Copyright (c) 2010 CCX(China) Co.,Ltd. All Rights Reserved.
 *
 * This software is the confidential and proprietary information of
 * CCX(China) Co.,Ltd. ("Confidential Information").
 * It may not be copied or reproduced in any manner without the express 
 * written permission of CCX(China) Co.,Ltd.
 *
 * Author: zhaosj
 * Date: 2010-10-20 下午04:40:14
 */
package com.ma.business;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.Date;

import com.ma.vo.MaHistory;
import com.ma.vo.MaPosition;
import com.ma.vo.TradeParameter;
import com.ma.vo.TradeValue;
import com.util.ConnUtil;

/**
 * @author zhaosj
 * 
 */
public class TradeBusiness {

	/**
	 * 获取股票交易信息
	 * 
	 * @param tp
	 */
	public void getTradeInfo(TradeParameter tp) {
		try {

			Connection con = ConnUtil.getConnection("localOracle");
			long innercode = 0l; // 股票内码

			// 获取所有股票内部代码
			String sql01 = "select distinct(innercode) from ma_stock order by 1";
			PreparedStatement pst1 = con.prepareStatement(sql01);
			ResultSet rs1 = pst1.executeQuery();

			String sql02 = "select id , innercode ,secucode,tradingday ,closeprice,"
					+ " (case when bs-bl > 0 then 1 when bs-bl < 0 then -1 else 0 end) buyvalue ,"
					+ " (case when ss-sl > 0 then 1 when ss-sl < 0 then -1 else 0 end) sellvalue " + " from ma_stock where innercode = ? "
					+ " order by innercode, tradingday ";

			PreparedStatement pst2 = con.prepareStatement(sql02);
			ResultSet rs2 = null;

			ArrayList<TradeValue> al = null;
			while (rs1.next()) {
				innercode = rs1.getLong("innercode");

				System.out.println("search stock's signal of tradable, innercode : " + innercode);

				pst2.setLong(1, innercode);
				rs2 = pst2.executeQuery();

				al = new ArrayList<TradeValue>();
				while (rs2.next()) {
					TradeValue tv = new TradeValue();
					tv.setId(rs2.getLong("id"));
					tv.setInnercode(rs2.getLong("innercode"));
					tv.setSecucode(rs2.getString("secucode"));
					tv.setTradingday(rs2.getDate("tradingday"));
					tv.setCloseprice(rs2.getDouble("closeprice"));
					tv.setBuyvalue(rs2.getInt("buyvalue"));
					tv.setSellvalue(rs2.getInt("sellvalue"));

					al.add(tv);
				}

				// 处理交易
				tradeBusiness(con, al, tp);

			}

			rs1.close();
			pst1.close();
			con.close();

		} catch (Exception e) {
			e.printStackTrace();
		}

		System.out.println("over!");
	}

	/**
	 * 股票交易
	 */
	public void tradeBusiness(Connection con, ArrayList<TradeValue> al, TradeParameter tp) {

		try {

			// 根据股票内码获取股票持仓信息
			String sql01 = "select * from ma_position where innercode = ?";
			PreparedStatement pst1 = con.prepareStatement(sql01);
			ResultSet rs1 = null;

			int buyValueP = 0;
			int buyValueN = 0;
			int sellValueP = 0;
			int sellValueN = 0;

			boolean buyFlagP = false; // 前日可买状态
			boolean buyFlagN = false; // 次日可买状态
			boolean sellFlagP = false; // 前日可卖状态
			boolean sellFlagN = false; // 次日可卖状态

			double br = tp.getBr(); // 买入比例
			double sr = tp.getSr(); // 卖出比例
			long quantity = tp.getQuantity(); // 原始资金量
			double bankroll = 0.0; // 可用资金量

			long innercode = 0l;
			long amount = 0l; // 持有股票数量
			long singleAmount = 0l; // 单笔交易股票数量
			double closeprice = 0.0; // 交易当日收盘价
			double commision = tp.getCommision();
			double stampDuty = tp.getStampDuty();

			TradeValue tvP = new TradeValue();	//前日
			TradeValue tvN = new TradeValue();	//次日
			TradeValue tvT = new TradeValue();	//第三日

			if (al.size() > 2) // 至少有3条记录
			{
				for (int i = 0; i < al.size() - 2; i++) {

					// 每次取3条记录，判断前两条均线的变化趋势，若均线产生了叉点，视为交易信号，第三日交易
					tvP = al.get(i);
					tvN = al.get(i + 1);
					tvT = al.get(i + 2);

					buyValueP = tvP.getBuyvalue();
					buyValueN = tvN.getBuyvalue();
					sellValueP = tvP.getSellvalue();
					sellValueN = tvN.getSellvalue();

					// 信号发生日和交易日的收盘价两者，取大值作为交易价格
					closeprice = Math.max(tvN.getCloseprice(), tvT.getCloseprice());

					innercode = tvP.getInnercode();

					if (i == 0 && buyValueP == 0) // 如果第一天为叉点，设为可买
					{
						buyFlagP = true;
					} else {
						if (buyValueP == 1) // 短线在上
							buyFlagP = true;
						else if (buyValueP == -1) // 短线在下
							buyFlagP = false;
						else
							// 叉点，状态同前一天
							buyFlagP = buyFlagN;
					}

					if (i == 0 && sellValueP == 0) // 如果第一天为叉点，设为可卖
					{
						sellFlagP = true;
					} else {
						if (sellValueP == 1) // 短线在上
							sellFlagP = false;
						else if (sellValueP == -1) // 长线在上
							sellFlagP = true;
						else
							// 叉点
							sellFlagP = sellFlagN;
					}

					if (buyValueN == 1) // 次日记录，买入控制
						buyFlagN = true;
					else if (buyValueN == -1)
						buyFlagN = false;
					else
						buyFlagN = buyFlagP;

					if (sellValueN == 1) // 次日记录，卖出控制
						sellFlagN = false;
					else if (sellValueN == -1)
						sellFlagN = true;
					else
						sellFlagN = sellFlagP;

					if (!buyFlagP && buyFlagN) // 可买入
					{
						pst1.setLong(1, innercode);
						rs1 = pst1.executeQuery(); // 检查是否已有该股票记录

						MaPosition mp = new MaPosition();

						if (rs1 != null && rs1.next()) // 存在该股票的记录
						{
							bankroll = rs1.getDouble("bankroll"); // 可用资金量

							singleAmount = (long) Math.floor(bankroll * br / closeprice / 100) * 100;
							if (singleAmount >= 100) // 足够买入
							{
								// 获取可用资金量、持有股票数量，购买后，更新记录
								mp.setId(rs1.getLong("id"));
								mp.setInnercode(rs1.getLong("innercode"));
								mp.setSecucode(rs1.getString("secucode"));
								mp.setTradingday(tvT.getTradingday());
								amount = rs1.getLong("amount"); // 现持有量

								amount += singleAmount;
								bankroll -= singleAmount * closeprice * (1 + commision);

								mp.setAmount(amount);
								mp.setBankroll(bankroll);

								// 更新持仓信息
								updateTradeInfo(con, mp);

								// 插入交易日志
								MaHistory mh = new MaHistory();
								mh.setSecucode(mp.getSecucode());
								mh.setVolume(singleAmount);
								mh.setDealing("B");
								mh.setTradingday(mp.getTradingday());
								mh.setPrice(closeprice);
								mh.setInnercode(innercode);
								mh.setAmount(amount);
								mh.setBankroll(bankroll);

								tradeLog(con, mh);

								System.out.println("Buy stock, innercode:" + mp.getInnercode()+", amount ："+singleAmount);
							} else {
								System.out.println("innercode ：" + innercode + ", it's lack in the useful bankroll to buy ONE HAND at least.");
							}

						} else // 如果没有该股票的记录，则插入第一条
						{
							mp.setInnercode(tvN.getInnercode());
							mp.setSecucode(tvN.getSecucode());
							singleAmount = (long) Math.floor(quantity * br / closeprice / 100) * 100;

							if (singleAmount >= 100) // 至少能够买入一手
							{
								bankroll = quantity - singleAmount * closeprice * (1 + commision);
								mp.setAmount(singleAmount);
								mp.setBankroll(bankroll);
								mp.setTradingday(tvT.getTradingday());

								// 插入首条持仓信息
								insertIntoTradeInfo(con, mp);

								// 插入交易日志
								MaHistory mh = new MaHistory();
								mh.setSecucode(mp.getSecucode());
								mh.setAmount(singleAmount);
								mh.setDealing("B");
								mh.setTradingday(mp.getTradingday());
								mh.setPrice(closeprice);
								mh.setInnercode(innercode);
								mh.setVolume(singleAmount);
								mh.setBankroll(bankroll);

								tradeLog(con, mh);

								System.out.println("Insert the first trade info, innercode ：" + innercode + ", amount ：" + singleAmount);

							} else {

								System.out.println("innercode ：" + innercode + ", it's lack in the useful bankroll to buy ONE HAND at least.");
							}
						}

						rs1.close();
					}

					if (!sellFlagP && sellFlagN) // 可卖出
					{
						pst1.setLong(1, innercode);
						rs1 = pst1.executeQuery(); // 检查是否已有该股票记录

						MaPosition mp = new MaPosition();

						if (rs1 != null && rs1.next()) {

							// 获取可用资金量、持有股票数量，卖出后，更新记录
							mp.setId(rs1.getLong("id"));
							mp.setInnercode(rs1.getLong("innercode"));
							mp.setSecucode(rs1.getString("secucode"));
							mp.setTradingday(tvT.getTradingday());

							bankroll = rs1.getDouble("bankroll"); // 可用资金量
							amount = rs1.getLong("amount"); // 现持有量

							singleAmount = (long) Math.floor(amount * sr);
							amount -= singleAmount;
							bankroll += singleAmount * closeprice * (1 - stampDuty - commision);

							mp.setAmount(amount);
							mp.setBankroll(bankroll);

							// 更新持仓信息
							updateTradeInfo(con, mp);

							// 插入交易日志
							MaHistory mh = new MaHistory();
							mh.setSecucode(mp.getSecucode());
							mh.setVolume(singleAmount);
							mh.setDealing("S");
							mh.setTradingday(mp.getTradingday());
							mh.setPrice(closeprice);
							mh.setInnercode(innercode);
							mh.setAmount(amount);
							mh.setBankroll(bankroll);

							tradeLog(con, mh);

							System.out.println("Sell stock, innercode:" + innercode+", amount："+singleAmount);

						} else // 如果没有该股票的记录，则忽略此次卖出信号
						{
							System.out.println("There isn't trade info, innercode ：" + innercode);
						}

						rs1.close();
					}
				}
			}
			pst1.close();

		} catch (Exception e) {

			e.printStackTrace();
		}
	}

	/**
	 * 插入股票交易信息
	 * 
	 * @param con
	 * @param mp
	 */
	public void insertIntoTradeInfo(Connection con, MaPosition mp) {
		try {

			long innercode = mp.getInnercode();
			String secucode = mp.getSecucode();
			double bankroll = mp.getBankroll();
			long amount = mp.getAmount();
			Date tradingday = mp.getTradingday();

			String sql = "insert into ma_position(id,innercode,secucode,bankroll,amount,tradingday) values(seq_ma_position.nextval,?,?,?,?,?)";

			PreparedStatement pst1 = con.prepareStatement(sql);
			pst1.setLong(1, innercode);
			pst1.setString(2, secucode);
			pst1.setDouble(3, bankroll);
			pst1.setLong(4, amount);
			pst1.setDate(5, new java.sql.Date(tradingday.getTime()));

			pst1.executeUpdate();

			pst1.close();

		} catch (Exception e) {

			e.printStackTrace();
		}
	}

	/**
	 * 更新股票交易信息
	 * 
	 * @param con
	 * @param mp
	 */
	public void updateTradeInfo(Connection con, MaPosition mp) {
		try {

			long id = mp.getId();
			long innercode = mp.getInnercode();
			long amount = mp.getAmount();
			double bankroll = mp.getBankroll();
			Date tradingday = mp.getTradingday();

			String sql = "update ma_position set amount = ? , bankroll = ? , tradingday = ? where id = ? and innercode = ?";

			PreparedStatement pst = con.prepareStatement(sql);
			pst.setLong(1, amount);
			pst.setDouble(2, bankroll);
			pst.setDate(3, (java.sql.Date) tradingday);
			pst.setLong(4, id);
			pst.setLong(5, innercode);

			pst.executeUpdate();

			pst.close();

		} catch (Exception e) {

			e.printStackTrace();
		}
	}

	/**
	 * 插入交易日志
	 * 
	 * @param con
	 * @param mh
	 */
	public void tradeLog(Connection con, MaHistory mh) {
		try {

			long innercode = mh.getInnercode();
			long amount = mh.getAmount();
			long volume = mh.getVolume();
			double price = mh.getPrice();
			double bankroll = mh.getBankroll();
			String secucode = mh.getSecucode();
			String dealing = mh.getDealing();
			Date tradingday = mh.getTradingday();

			String sql = "insert into ma_history (id,innercode,secucode,volume,dealing,price,amount,bankroll,tradingday) values(seq_ma_history.nextval,?,?,?,?,?,?,?,?)";

			PreparedStatement pst = con.prepareStatement(sql);
			pst.setLong(1, innercode);
			pst.setString(2, secucode);
			pst.setLong(3, volume);
			pst.setString(4, dealing);
			pst.setDouble(5, price);
			pst.setLong(6, amount);
			pst.setDouble(7, bankroll);
			pst.setDate(8, (java.sql.Date) tradingday);

			pst.executeUpdate();

			pst.close();

		} catch (Exception e) {

			e.printStackTrace();
		}

	}

}
